{
 "cells": [
  {
   "cell_type": "raw",
   "metadata": {
    "vscode": {
     "languageId": "raw"
    }
   },
   "source": [
    "---\n",
    "title: Python integration\n",
    "sidebar_position: 1\n",
    "description: Using Python and Mojo together.\n",
    "---"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Mojo is still in early development and many Python features are not yet\n",
    "implemented. You can't currently write everything in Mojo that you can write in\n",
    "Python. And Mojo doesn't have its own ecosystem of packages yet.\n",
    "\n",
    "To help bridge this gap, Mojo lets you import Python modules, call Python \n",
    "functions, and interact with Python objects from Mojo code. The Python code\n",
    "runs in a standard Python interpreter (CPython), so your existing Python code\n",
    "doesn't need to change.\n",
    "\n",
    "## Create a Python environment\n",
    "\n",
    "To successfully integrate Python code with your Mojo project, your environment\n",
    "must have a compatible Python runtime installed along with any additional\n",
    "Python packages that you want to use. Currently, you can create a compatible\n",
    "environment in a couple of ways:\n",
    "\n",
    "- We recommend that you use [Magic](/magic), our package manager and\n",
    "  virtual environment manager for MAX and Mojo projects. To use Magic to create\n",
    "  and manage the virtual environment for your Mojo/Python project, first\n",
    "  follow the instructions in [Install Magic](/magic/#install-magic).\n",
    "  Then you can create a new Mojo project like this:\n",
    "\n",
    "  ```sh\n",
    "  magic init my-mojo-project --format mojoproject\n",
    "  ```\n",
    "\n",
    "  After creating the project, you can enter the project and install any\n",
    "  dependencies, for example [NumPy](https://numpy.org/):\n",
    "\n",
    "  ```sh\n",
    "  cd my-mojo-project\n",
    "  ```\n",
    "\n",
    "  ```sh\n",
    "  magic add \"numpy>=2.0\"\n",
    "  ```\n",
    "\n",
    "- Alternatively, you can also add MAX and Mojo to a\n",
    "  [conda](https://docs.conda.io/projects/conda/en/latest/index.html) project.\n",
    "  To do so, follow the steps in [Add MAX/Mojo to a conda project](/magic/conda).\n",
    "\n",
    "- It's also possible to convert an existing conda project to Magic as documented\n",
    "  in [Migrate a conda project to Magic](/magic/#migrate-a-conda-project-to-magic).\n",
    "\n",
    "## Import a Python module\n",
    "\n",
    "To import a Python module in Mojo, just call \n",
    "[`Python.import_module()`](/mojo/stdlib/python/python/Python#import_module) \n",
    "with the module name. The following shows an example of importing the standard\n",
    "Python [NumPy](https://numpy.org/) package:\n",
    "\n",
    "```mojo\n",
    "from python import Python\n",
    "\n",
    "def main():\n",
    "    # This is equivalent to Python's `import numpy as np`\n",
    "    np = Python.import_module(\"numpy\")\n",
    "\n",
    "    # Now use numpy as if writing in Python\n",
    "    array = np.array([1, 2, 3])\n",
    "    print(array)\n",
    "```\n",
    "\n",
    "Running this program produces the following output:\n",
    "\n",
    "```\n",
    "[1 2 3]\n",
    "```"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Assuming that you have the NumPy package installed in your\n",
    "[environment](#create-a-python-environment), this imports NumPy and you can use any\n",
    "of its features.\n",
    "\n",
    "A few things to note:\n",
    "\n",
    "- The `import_module()` method returns a reference to the module in the form of\n",
    "  a [`PythonObject`](/mojo/stdlib/python/python_object/PythonObject)\n",
    "  wrapper. You must store the reference in a variable and then use it as shown\n",
    "  in the example above to access functions, classes, and other objects defined\n",
    "  by the module. See [Mojo wrapper objects](/mojo/manual/python/types#mojo-wrapper-objects)\n",
    "  for more information about the `PythonObject` type.\n",
    "\n",
    "- Currently, you cannot import individual members (such as a single Python class\n",
    "  or function). You must import the whole Python module and then access members\n",
    "  through the module name.\n",
    "\n",
    "- Mojo doesn't yet support top-level code, so the `import_module()` call must\n",
    "  be inside another method. This means you may need to import a module multiple\n",
    "  times or pass around a reference to the module. This works the same way as \n",
    "  Python: importing the module multiple times won't run the initialization\n",
    "  logic more than once, so you don't pay any performance penalty.\n",
    "\n",
    "- `import_module()` may raise an exception (for example, if the module isn't\n",
    "  installed). If you're using it inside an `fn` function, you need to either\n",
    "  handle errors (using a `try/except` clause), or add the `raises` keyword to\n",
    "  the function signature. You'll also see this when calling Python functions\n",
    "  that may raise exceptions. (Raising exceptions is much more common in Python\n",
    "  code than in the Mojo standard library, which \n",
    "  [limits their use for performance reasons](/mojo/roadmap#the-standard-library-has-limited-exceptions-use).)\n",
    "\n",
    "\n",
    ":::caution\n",
    "\n",
    "[`mojo build`](/mojo/cli/build) doesn't include the Python packages used by\n",
    "your Mojo project. Instead, Mojo loads the Python interpreter and Python\n",
    "packages at runtime, so they must be provided in the environment where you run\n",
    "the Mojo program (such as inside the Magic environment where you built the\n",
    "executable). For more information, see the section above to [create a Python\n",
    "environment](#create-a-python-environment).\n",
    "\n",
    ":::"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Import a local Python module\n",
    "\n",
    "If you have some local Python code you want to use in Mojo, just add\n",
    "the directory to the Python path and then import the module.\n",
    "\n",
    "For example, suppose you have a Python file named `mypython.py`:\n",
    "\n",
    "```python title=\"mypython.py\"\n",
    "import numpy as np\n",
    "\n",
    "def gen_random_values(size, base):\n",
    "    # generate a size x size array of random numbers between base and base+1\n",
    "    random_array = np.random.rand(size, size)\n",
    "    return random_array + base\n",
    "```\n",
    "\n",
    "Here's how you can import it and use it in a Mojo file:\n",
    "\n",
    "```mojo title=\"main.mojo\"\n",
    "from python import Python\n",
    "\n",
    "def main():\n",
    "    Python.add_to_path(\"path/to/module\")\n",
    "    mypython = Python.import_module(\"mypython\")\n",
    "\n",
    "    values = mypython.gen_random_values(2, 3)\n",
    "    print(values)\n",
    "```\n",
    "\n",
    "Both absolute and relative paths work with \n",
    "[`add_to_path()`](/mojo/stdlib/python/python/Python#add_to_path). For example,\n",
    "you can import from the local directory like this:\n",
    "\n",
    "```mojo\n",
    "Python.add_to_path(\".\")\n",
    "```\n",
    "\n",
    "## Call Mojo from Python\n",
    "\n",
    "As shown above, you can call out to Python modules from Mojo. However, there's \n",
    "currently no way to do the reverse—import Mojo modules from Python or call Mojo\n",
    "functions from Python.\n",
    "\n",
    "This may present a challenge for using certain modules. For example, many UI \n",
    "frameworks have a main event loop that makes callbacks to user-defined code\n",
    "in response to UI events. This is sometimes called an \"inversion of control\" \n",
    "pattern. Instead of your application code calling *in* to a library, the \n",
    "framework code calls *out* to your application code.\n",
    "\n",
    "This pattern doesn't work because you can't pass Mojo callbacks to a Python \n",
    "module.\n",
    "\n",
    "For example, consider the popular [Tkinter package](https://docs.python.org/3/library/tkinter.html). \n",
    "The typical usage for Tkinter is something like this:\n",
    "\n",
    "- You create a main, or \"root\" window for the application.\n",
    "- You add one or more UI widgets to the window. The widgets can have associated\n",
    "  callback functions (for example, when a button is pushed).\n",
    "- You call the root window's `mainloop()` method, which listens for events, \n",
    "  updates the UI, and invokes callback functions. The main loop keeps running\n",
    "  until the application exits.\n",
    "\n",
    "Since Python can't call back into Mojo, one alternative is to have the Mojo\n",
    "application drive the event loop and poll for updates. The following example\n",
    "uses Tkinter, but the basic approach can be applied to other packages.\n",
    "\n",
    "First you create a Python module that defines a Tkinter interface, with a window\n",
    "and single button:\n",
    "\n",
    "```python title=\"myapp.py\"\n",
    "import tkinter as tk\n",
    "\n",
    "class App:\n",
    "    def __init__(self):\n",
    "        self._root = tk.Tk()\n",
    "        self.clicked = False\n",
    "\n",
    "    def click(self):\n",
    "        self.clicked = True\n",
    "\n",
    "    def create_button(self, button_text: str):\n",
    "        button = tk.Button(\n",
    "            master=self._root,\n",
    "            text=button_text,\n",
    "            command=self.click\n",
    "        )\n",
    "        button.place(relx=0.5, rely=0.5, anchor=tk.CENTER)\n",
    "\n",
    "    def create(self, res: str):\n",
    "        self._root.geometry(res)\n",
    "        self.create_button(\"Hello Mojo!\")\n",
    "\n",
    "    def update(self):\n",
    "        self._root.update()\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can call this module from Mojo like this:\n",
    "\n",
    "```mojo title=\"main.mojo\"\n",
    "from python import Python\n",
    "\n",
    "def button_clicked():\n",
    "    print(\"Hi from a Mojo🔥 fn!\")\n",
    "\n",
    "def main():\n",
    "    Python.add_to_path(\".\")\n",
    "    app = Python.import_module(\"myapp\").App()\n",
    "    app.create(\"800x600\")\n",
    "\n",
    "    while True:\n",
    "        app.update()\n",
    "        if app.clicked:\n",
    "            button_clicked()\n",
    "            app.clicked = False\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Instead of the Python module calling the Tkinter `mainloop()` method, the Mojo \n",
    "code calls the `update()` method in a loop and checks the `clicked` attribute \n",
    "after each update.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Mojo",
   "language": "mojo",
   "name": "mojo-jupyter-kernel"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "mojo"
   },
   "file_extension": ".mojo",
   "mimetype": "text/x-mojo",
   "name": "mojo"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
